#include "myfilelog.hxx"
#include "loghelper.hxx"
#include "taskdefs.h"
#include "was_api.h"
#include "bridgemessage.hxx"
#include "secudpneter.hxx"
#include "priv/groupmgr.hxx"
#include "priv/tstatusmgr.hxx"
#include "priv/stconn.hxx"

STConn::STConn(string remote_host,tUInt32 remote_port,GroupMgr* groupmgr,TStatusMgr* tstatusmgr,FileLog* filelog)
:	TConn(remote_host,remote_port,filelog),
	_groupmgr(groupmgr),
	_tstatusmgr(tstatusmgr),
	_secudpneter(NULL)
{
}
STConn::~STConn()
{
}

tUInt32 STConn::Login(tUInt32 myid,string myname)
{
	LOGDEBUG("STConn::Login:Logining...");

	tUInt32 taskid = GetATId();

	/**
	 * Create the request message
	 */
	tByte* msg;
	tUInt32 len;
	string ip	= SecUdpNeter::PeerLocalAddress();

	tChar myid_str[10] = {0};
	sprintf_s(myid_str, "%d", myid);

	msg = was_write_msg(&len, E_WAS_MSG_FIRST_PART, E_WAS_MSG_LOGIN_REQ,taskid, X_WAS_RSPC_NONE,
								E_WAS_TAG_ID_MYID, strlen(myid_str), myid_str, 
								E_WAS_TAG_ID_MYNM, myname.length(), myname.c_str(),
								E_WAS_TAG_ID_IHOT, ip.length(), ip.c_str(),
								E_WAS_TAG_ID_NONE);

	if (!msg)
	{
		LOGWARNING("STConn::InviteMember:Error happened while was_write_msg(...).");
		return 0;
	}

	tBridgeMsg* bmsg = new tBridgeMsg();

	bmsg->lom = len;
	bmsg->mpi = E_WAS_MSG_FIRST_PART;
	bmsg->tid = taskid;
	bmsg->rspc= X_WAS_RSPC_NONE;
	bmsg->msg = msg;
	bmsg->tom = E_WAS_MSG_LOGIN_REQ;

	/*
	 * register the task
	 */
	_tstatusmgr->RegisterTStatus(CENTRALS_USER_ID,taskid,TStatusMgr::TASK_TYPE_LOGIN);

	SaveOMsg(bmsg);
	
	return taskid;
}
tUInt32 STConn::InvitePeer(tUInt32 myid,tUInt32 userid)
{
	tUInt32 taskid = GetATId();

	LOGDEBUG("STConn::InvitePeer:inviting peer #"<<userid<<"...");

	/**
	 * Create the request message
	 */
	tUInt32 len = 0;

	tChar myid_str[10] = {0};
	sprintf_s(myid_str, "%d", myid);
	tChar userid_str[10] = {0};
	sprintf_s(userid_str, "%d", userid);

	tByte* msg = was_write_msg(&len, E_WAS_MSG_FIRST_PART, E_WAS_MSG_INVITEPEER_REQ,taskid, X_WAS_RSPC_NONE,
								E_WAS_TAG_ID_MYID, strlen(myid_str), myid_str, 
								E_WAS_TAG_ID_MBID, strlen(userid_str), userid_str,
								E_WAS_TAG_ID_NONE);

	if (!msg)
	{
		LOGWARNING("STConn::InviteMember:Error happened while was_write_msg(...).");
		return 0;
	}

	tBridgeMsg* bmsg = new tBridgeMsg();

	bmsg->lom = len;
	bmsg->mpi = E_WAS_MSG_FIRST_PART;
	bmsg->tid = taskid;
	bmsg->rspc= X_WAS_RSPC_NONE;
	bmsg->msg = msg;
	bmsg->tom = E_WAS_MSG_INVITEPEER_REQ;

	/*
	 * register the task
	 */
	_tstatusmgr->RegisterTStatus(CENTRALS_USER_ID,taskid,TStatusMgr::TASK_TYPE_INVITE_VOICE);

	SaveOMsg(bmsg);
	
	return taskid;
}
void STConn::NotifyAddNewMember(tUInt32 new_id,string new_name,string new_host,tUInt32 new_udp_port)
{
	//loop
}
tUInt32 STConn::TransferRequest(tUInt32 userid,string file_path_and_name)
{
	tUInt32 taskid = GetATId();

	return taskid;
}
tBool STConn::RecvBridgeMsg(void)
{
	tBridgeMsg* imsg;

	if (!_secudpneter->Recv(&imsg))
		return FALSE;
	
	SaveIMsg(imsg);

	return TRUE;
}
tBool STConn::SendBridgeMsg(void)
{
	/** 
	 * Check if any message sending failed,and update status for them.do it later.
	 */

	if (!_omsg)
	{
		if (!(_omsg = _omsg_que.Pop()))
			return FALSE;
	}

	_secudpneter->SendTo(_omsg,_remote_host,_remote_port);

	_tstatusmgr->UpdateTStatus(CENTRALS_USER_ID,_omsg->tid,TStatusMgr::TASK_STATE_REQUEST_SENT);

	/** 
	 * As soon as sent,release the omsg.but we don't release for secudpneter,for here
	 * is just pushing the omsg into the out going que.Leave it to the secudpneter.
	 */
	//ReleaseBridgeMsg(_omsg);

	_omsg = NULL;

	return TRUE;
}
void STConn::PreRunning(void)
{
	LOGDEBUG("STConn::PreRunning:Begin.");

	_secudpneter = new SecUdpNeter(10000,_filelog);

	_secudpneter->Start();

	LOGDEBUG("STConn::PreRunning:End.");
}
void STConn::AfterRunning(void)
{
	LOGDEBUG("STConn::AfterRunning:Begin.");

	_secudpneter->Shutdown();

	tUInt32 counter=0;
	while (counter<100 && (_secudpneter))
	{
		if (!_secudpneter && _secudpneter->State()==Thread::FINISHED_STATE)
		{
			_secudpneter->Join();
			delete _secudpneter;
			_secudpneter = NULL;
		}

		SleepMs(100);

		counter++;
	}

	if (counter==100)
	{
		LOGWARNING("STConn::AfterRunning:SecUdpNeter won't shutdown,sorry but we'll not join any longer,forcing shutting it down.");
		delete _secudpneter;
		_secudpneter = NULL;
	}

	LOGDEBUG("STConn::AfterRunning:End.");
}
tBool STConn::ProcessIMsg(void)
{
	if (!_imsg)
	{
		if (!(_imsg = _imsg_que.Pop()))
			return FALSE;
	}

	switch (_imsg->tom)
	{
	case E_WAS_MSG_LOGIN_CNF:
		ProcessLoginCNFMsg(_imsg);
		break;
	case E_WAS_MSG_INVITEPEER_REQ:
		ProcessInviteVoiceREQMsg(_imsg);
		break;
	case E_WAS_MSG_INVITEPEER_CNF:
		ProcessInviteVoiceCNFMsg(_imsg);
		break;
	case E_WAS_MSG_ADDMEMBER_CNF:
	case E_WAS_MSG_TRANSFER_CNF:
		break;
	default:
		LOGWARNING("STConn::ProcessIMsg:Unknown imsg #"<<_imsg->tid<<" from "<<_imsg->rhost<<"::"<<_imsg->rport<<",abandon it.");
		ReleaseBridgeMsg(_imsg);
		break;
	}

	/*As it's processed.leave it to the Process... respectively.*/
	_imsg = NULL;

	return TRUE;
}
void STConn::ProcessLoginCNFMsg(tBridgeMsg* imsg)
{
	TStatusMgr::tTState tstate = TStatusMgr::TASK_STATE_UNDEFINED;

	switch (imsg->rspc)
	{
	case X_WAS_RSPC_OK:
		LOGDEBUG("STConn::ProcessInviteVoiceCNFMsg:Positively confirmed.");
		tstate = TStatusMgr::TASK_STATE_REQUEST_CONFIRMED;
		break;
	case X_WAS_RSPC_CENTRALS_AUTHORIZATION_FAILED:
		LOGWARNING("STConn::ProcessInviteVoiceCNFMsg:Authorization failed by CentralS.");
		tstate = TStatusMgr::TASK_STATE_REQUEST_REJECTED;
		break;
	case X_WAS_RSPC_CENTRALS_FORWARDED:
		LOGDEBUG("STConn::ProcessInviteVoiceCNFMsg:The current CentralS is busy,turn to another CentralS.");
		tstate = TStatusMgr::TASK_STATE_ON_GOING;
		break;
	case X_WAS_RSPC_CENTRALS_BUSY:
		LOGDEBUG("STConn::ProcessInviteVoiceCNFMsg:The CentralS system is busy,login failed.");
		tstate = TStatusMgr::TASK_STATE_REQUEST_REJECTED;
		break;
	case X_WAS_RSPC_BAD_REQUEST:
		LOGWARNING("STConn::ProcessInviteVoiceCNFMsg:Logically error,X_WAS_RSPC_BAD_REQUEST returned from CentralS.");
		tstate = TStatusMgr::TASK_STATE_ERROR_HAPPENED;
		break;
	default:
		LOGWARNING("STConn::ProcessLoginCNFMsg:Unknown rspc from CentralS'"<<imsg->rspc<<"'.");
		tstate = TStatusMgr::TASK_STATE_ERROR_HAPPENED;
		break;
	}

	if (!_tstatusmgr->UpdateTStatus(CENTRALS_USER_ID,imsg->tid,tstate))
		LOGWARNING("STConn::ProcessLoginCNFMsg:Failed to UpdateTStatus for login cnf imsg.");

	ReleaseBridgeMsg(imsg);
}
void STConn::ProcessInviteVoiceREQMsg(tBridgeMsg* imsg)
{
	tUInt32 requesteeid;
	tChar* requesteeid_str = GetDataFromMsg(imsg,E_WAS_TAG_ID_MYID);
	tChar* username = GetDataFromMsg(imsg,E_WAS_TAG_ID_MYNM);

	if (requesteeid_str)	requesteeid = atoi(requesteeid_str);

	if (!requesteeid || !username)
	{
		/** Actually it won't happen, just here for debug.*/
		LOGWARNING("STConn::ProcessInviteVoiceREQMsg:Failed to extract enough from imsg.");
		return;
	}

	LOGDEBUG("STConn::ProcessInviteVoiceREQMsg:User '"<<username<<" #"<<requesteeid<<"' is inviting me to talk.");

	/*
	 * Register the status.
	 */
	_tstatusmgr->RegisterTStatus(requesteeid,imsg->tid,TStatusMgr::TASK_TYPE_INVITE_VOICE,TStatusMgr::TASK_STATE_REQUEST_REQUESTED,imsg);

	/*temporily for test.*/
	AnswerInvitePeerREQ(requesteeid,imsg->tid,TRUE);
}
void STConn::ProcessInviteVoiceCNFMsg(tBridgeMsg* imsg)
{
	tUInt32 userid;
	tChar* userid_str;
	tChar* username;
	tChar* public_ip;
	tChar* internal_ip;
	tUInt32 public_pt_udp_port;
	tChar* public_pt_udp_port_str;
	tUInt32 internal_pt_udp_port;
	tChar* internal_pt_udp_port_str;

	TStatusMgr::tTState tstate = TStatusMgr::TASK_STATE_UNDEFINED;

	switch (imsg->rspc)
	{
	case X_WAS_RSPC_OK:
		tstate = TStatusMgr::TASK_STATE_REQUEST_CONFIRMED;

		userid_str					= GetDataFromMsg(imsg,E_WAS_TAG_ID_MYID);
		username					= GetDataFromMsg(imsg,E_WAS_TAG_ID_MYNM);
		public_ip					= GetDataFromMsg(imsg,E_WAS_TAG_ID_PHOT);
		internal_ip					= GetDataFromMsg(imsg,E_WAS_TAG_ID_IHOT);
		public_pt_udp_port_str		= GetDataFromMsg(imsg,E_WAS_TAG_ID_PPOT);
		internal_pt_udp_port_str	= GetDataFromMsg(imsg,E_WAS_TAG_ID_IPOT);

		if (userid_str)					userid = atoi(userid_str);
		if (public_pt_udp_port_str)		public_pt_udp_port = atoi(public_pt_udp_port_str);
		if (internal_pt_udp_port_str)	internal_pt_udp_port = atoi(internal_pt_udp_port_str);

		LOGDEBUG("STConn::ProcessInviteVoiceCNFMsg:Peer "<<username<<" #"<<userid<<"' accepted my invitation.");

		_groupmgr->AddMember2Add(userid,username,public_ip,public_pt_udp_port,internal_ip,internal_pt_udp_port);
		break;
	case X_WAS_RSPC_MEMBER_REJECTED:
		LOGDEBUG("STConn::ProcessInviteVoiceCNFMsg:The peer requestee rejected my invitation to talk.");
		tstate = TStatusMgr::TASK_STATE_REQUEST_REJECTED;
		break;
	case X_WAS_RSPC_MEMBER_UNAVAILABLE:
		LOGDEBUG("STConn::ProcessInviteVoiceCNFMsg:The requestee is not available.");
		tstate = TStatusMgr::TASK_STATE_REQUEST_UNAVAILABLE;
		break;
	case X_WAS_RSPC_BAD_REQUEST:
		LOGWARNING("STConn::ProcessInviteVoiceCNFMsg:X_WAS_RSPC_BAD_REQUEST returned from CentralS.");
	case X_WAS_RSPC_INTERNAL_SERVER_ERROR:
		LOGWARNING("STConn::ProcessInviteVoiceCNFMsg:X_WAS_RSPC_INTERNAL_SERVER_ERROR returned from CentralS.");
	default:
		LOGWARNING("STConn::ProcessInviteVoiceCNFMsg:Unknown rspc returned '"<<imsg->rspc<<"'.");
		tstate = TStatusMgr::TASK_STATE_ERROR_HAPPENED;
		break;
	}

	if (!_tstatusmgr->UpdateTStatus(CENTRALS_USER_ID,imsg->tid,tstate))
		LOGWARNING("STConn::ProcessLoginCNFMsg:Failed to UpdateTStatus for invitation cnf imsg.");

	ReleaseBridgeMsg(imsg);
}
void STConn::AnswerInvitePeerREQ(tUInt32 userid,tUInt32 tid,tBool decision=TRUE)
{
	tByte* msg;
	tUInt32 len;
	tUInt32 rspc;

	tUInt32 myid=0;
	tUInt32 public_pt_udp_port=0;
	tUInt32 internal_pt_udp_port=0;

	tChar* myid_str;
	tChar* userid_str;
	tChar* username;
	tChar* public_ip;
	tChar* internal_ip;

	TStatusMgr::tTaskType tasktype;
	TStatusMgr::tTState tstate = TStatusMgr::TASK_STATE_UNDEFINED;;
	float	progress=0;
	tBridgeMsg* reqestmsg = NULL;

	LOGDEBUG("STConn::AnswerInvitePeerREQ:Confirm the invitation to talk.");

	/** it won't happen if code is logically right,just for debug here.*/
	if (!_tstatusmgr->QueryTStatus(userid,tid,tasktype,tstate,progress,&reqestmsg))
		LOGERROR("STConn::AnswerInvitePeerREQ:Failed to QueryTStatus.");

	if(!decision)
	{
		rspc = X_WAS_RSPC_MEMBER_REJECTED;
		tstate = TStatusMgr::TASK_STATE_REQUEST_REJECTED;
	}
	else
	{
		userid_str = GetDataFromMsg(reqestmsg,E_WAS_TAG_ID_MYID);
		myid_str = GetDataFromMsg(reqestmsg,E_WAS_TAG_ID_MBID);

		username = GetDataFromMsg(reqestmsg,E_WAS_TAG_ID_MYNM);
		public_ip = GetDataFromMsg(reqestmsg,E_WAS_TAG_ID_PHOT);
		internal_ip = GetDataFromMsg(reqestmsg,E_WAS_TAG_ID_IHOT);
		
		tChar* public_pt_udp_port_str = GetDataFromMsg(reqestmsg,E_WAS_TAG_ID_PPOT);
		tChar* internal_pt_udp_port_str = GetDataFromMsg(reqestmsg,E_WAS_TAG_ID_IPOT);

		assert(atoi(userid_str)==userid);

		if (myid_str)					myid = atoi(myid_str);
		if (userid_str)					userid = atoi(userid_str);
		if (public_pt_udp_port_str)		public_pt_udp_port = atoi(public_pt_udp_port_str);
		if (internal_pt_udp_port_str)	internal_pt_udp_port = atoi(internal_pt_udp_port_str);

		if (!myid || !userid || !username || !public_pt_udp_port || !internal_pt_udp_port || !public_ip || !internal_ip)
		{
			rspc = X_WAS_RSPC_INTERNAL_SERVER_ERROR;
			tstate = TStatusMgr::TASK_STATE_ERROR_HAPPENED;
		}
		else
		{
			rspc = X_WAS_RSPC_OK;
			tstate = TStatusMgr::TASK_STATE_REQUEST_CONFIRMED;
		}
	}

	msg = was_write_msg(&len, E_WAS_MSG_FIRST_PART, E_WAS_MSG_INVITEPEER_CNF,reqestmsg->tid, rspc,
								E_WAS_TAG_ID_MBID,strlen(userid_str),userid_str,
								E_WAS_TAG_ID_MYID,strlen(myid_str),myid_str,
								E_WAS_TAG_ID_NONE);
	if (!msg)
	{
		LOGWARNING("STConn::AnswerInvitePeerREQ:Error happened while was_write_msg(...).");
		return;
	}

	tBridgeMsg* bmsg = new tBridgeMsg();

	bmsg->lom = len;
	bmsg->mpi = E_WAS_MSG_FIRST_PART;
	bmsg->tid = reqestmsg->tid;
	bmsg->rspc= rspc;
	bmsg->msg = msg;
	bmsg->tom = E_WAS_MSG_INVITEPEER_CNF;

	SaveOMsg(bmsg);

	if (!_tstatusmgr->UpdateTStatus(userid,tid,tstate))
		LOGWARNING("STConn::AnswerInvitePeerREQ:Failed to UpdateTStatus for the request to talk.");

	if (rspc==X_WAS_RSPC_OK)
		_groupmgr->AddMember2Add(userid,username,public_ip,public_pt_udp_port,internal_ip,internal_pt_udp_port);
}